package conson.tech.manage.coupons.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import conson.tech.core.exception.BaseException;
import conson.tech.core.response.Response;
import conson.tech.manage.common.constant.BusinessMessage;
import conson.tech.manage.common.util.TimeUtils;
import conson.tech.manage.coupons.constant.CouponsConstants;
import conson.tech.manage.coupons.entity.dto.CouponDto;
import conson.tech.manage.coupons.entity.dto.CouponReturnDto;
import conson.tech.manage.coupons.entity.dto.UserCouponListDto;
import conson.tech.manage.coupons.entity.dto.UserGrantCouponDto;
import conson.tech.manage.coupons.entity.pojo.*;
import conson.tech.manage.coupons.entity.vo.*;
import conson.tech.manage.coupons.mapper.*;
import conson.tech.manage.coupons.service.AppCouponsService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * InterfaceName: AppCouponsServiceImpl
 * Description: 优惠券业务接口
 * Author: XYF
 * Date: 2020/7/25 10:07
 * Version: 1.0
 */
@Slf4j
@Service
public class AppCouponsServiceImpl implements AppCouponsService {

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private CouponsUsersMapper couponsUsersMapper;

    @Autowired
    private ActivityCouponsMapper activityCouponsMapper;

    @Autowired
    private CouponsUsersLogMapper couponsUsersLogMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private CouponsBatchMapper couponsBatchMapper;

    @Autowired
    private CouponsCenterMapper couponsCenterMapper;

    @Autowired
    private CouponsUsingRangeMapper couponsUsingRangeMapper;

    /**
     * 按活动id获取关联的优惠券列表
     *
     * @param pageIndex
     * @param pageSize
     * @param id
     * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page<conson.tech.manage.app.entity.vo.ActivityCouponPageVo>
     */
    @Override
    public Page<UserCouponDetailVo> couponActivityList(Integer pageIndex, Integer pageSize, Long id) {

        log.info("按活动id获取关联的优惠券列表开始");
        Page<UserCouponDetailVo> page = new Page<>(pageIndex, pageSize);
        log.info("按活动id获取关联的优惠券列表完成");
        return this.couponsUsersMapper.couponActivityList(page, id);
    }

    /**
     * 用户领用优惠券
     *
     * @param currentUserId
     * @param dto
     * @return void
     */
    @Override
    @Transactional
    public Response grantCoupon(Long currentUserId, String currentUserName, UserGrantCouponDto dto) {

        log.info("用户领用优惠券开始");

        UserGrantCouponVo userGrantCouponVo = null;
//          TODO 远程访问用户模块 校验用户是否存在

        //         校验活动是否存在且在活动有效期
        Activity activity = activityMapper.selectOne(new QueryWrapper<Activity>().eq("id", dto.getActivityId()));
        if (activity == null) {
            throw new BaseException(BusinessMessage.ACTIVITY_NOT_EXISTS_ERROR);
        }

//        校验活动状态
        if (activity.getStatus() != 2) {
            throw new BaseException(BusinessMessage.ACTIVITY_STATUSN_NOT_ON_ERROR);
        }
        CouponsBatch couponsBatch = this.couponsBatchMapper.selectById(dto.getBatchId());
        if (couponsBatch == null) {
            throw new BaseException(BusinessMessage.COUPONS_NOT_EXISTS_ERROR);
        }
//        校验优惠券活动是否开始
        ActivityCoupons activityCoupons = this.activityCouponsMapper.selectOne(new QueryWrapper<ActivityCoupons>()
                .eq("batch_id", dto.getBatchId())
                .eq("activity_id", dto.getActivityId()));
//         时间判断
        boolean result = (LocalDate.now().isEqual(activityCoupons.getStartTime()) || LocalDate.now().isAfter(activityCoupons.getStartTime())) && (LocalDate.now().isBefore(activityCoupons.getEndTime()) || LocalDate.now().isEqual(activityCoupons.getEndTime()));
        if (!result) {
            //  领券活动开始，但是优惠券批次活动还未开始
            throw new BaseException(BusinessMessage.ACTIVITY_COUPON_STATUS_NOT_START_ERROR);
        }

//        校验用户领取的次数，是否已经超过了活动领取规定次数
        Integer count = this.couponsUsersMapper.selectCount(new QueryWrapper<CouponsUsers>()
                .eq("user_id", currentUserId)
                .eq("batch_id", activityCoupons.getBatchId())
                .eq("activity_id", activityCoupons.getActivityId()));
        if (count.equals(activityCoupons.getLimitNum())) {
            throw new BaseException(BusinessMessage.COUPON_COUNT_LIMIT_ERROR);
        }
        //      分布式锁
        RLock lock = redissonClient.getLock(CouponsConstants.GRANT_COUPON_LOCK);
//        加锁
        lock.lock();
        try {
//        校验活动库存是否充足
            if (!redisTemplate.hasKey(CouponsConstants.ACTIVITY_COUPON_PREFIX + activity.getId() + ":" + dto.getBatchId())) {
                throw new BaseException(BusinessMessage.ACTIVITY_COUPON_NOT_ENOUGH_GRANT_ERROR);
            }
            String activityStock = redisTemplate.opsForValue().get(CouponsConstants.ACTIVITY_COUPON_PREFIX + activity.getId() + ":" + dto.getBatchId());
            if (activityStock != null) {
                //          从直接领取活动中扣除活动库存
                int newStock = Integer.parseInt(activityStock) - 1;
                redisTemplate.opsForValue().set(CouponsConstants.ACTIVITY_COUPON_PREFIX + activity.getId() + ":" + dto.getBatchId(), String.valueOf(newStock));
            }
        } finally {
//            释放分布式锁
            lock.unlock();
            //           给用户发放优惠券，生成优惠券码
            //        1.生成优惠券码
            String code = TimeUtils.getGuid();
            CouponsUsers couponsUsers = new CouponsUsers();
            couponsUsers.setCouponsCode(code);
            couponsUsers.setBatchId(dto.getBatchId());
            couponsUsers.setActivityId(activity.getId());
            couponsUsers.setCreateTime(LocalDateTime.now());
            couponsUsers.setCreateBy(currentUserId);
            couponsUsers.setState(0);
//            设置优惠券的有效日期
            if (couponsBatch.getTimeLimit() == 1) {
                couponsUsers.setStartTime(LocalDateTime.now());
                couponsUsers.setEndTime(LocalDateTime.now().plusDays(couponsBatch.getRelativeTime()));
            }
            if (couponsBatch.getTimeLimit() == 0) {
                couponsUsers.setStartTime(couponsBatch.getStartTime());
                couponsUsers.setEndTime(couponsBatch.getEndTime());
            }
            couponsUsers.setUserId(currentUserId);
            couponsUsersMapper.insert(couponsUsers);

            // 插入优惠券发放日志表
            CouponsUsersLog couponsUsersLog = new CouponsUsersLog();
            couponsUsersLog.setCreateTime(LocalDateTime.now());
            couponsUsersLog.setCouponsId(couponsUsers.getId());
            couponsUsersLog.setCouponsCode(couponsUsers.getCouponsCode());
            couponsUsersLog.setCouponsState(0);
            couponsUsersLog.setCreateName(currentUserName);
            couponsUsersLog.setOperateDesc("用户领取优惠券");
            this.couponsUsersLogMapper.insert(couponsUsersLog);

//            将结果返回
            userGrantCouponVo = new UserGrantCouponVo();
            userGrantCouponVo.setCouponsCode(couponsUsers.getCouponsCode());
            userGrantCouponVo.setStartTime(couponsUsers.getStartTime());
            userGrantCouponVo.setEndTime(couponsUsers.getEndTime());

            log.info("用户领用优惠券结束");
        }
        return Response.success(userGrantCouponVo);
    }


    /**
     * 获取用户我的优惠券
     * TODO 先查一遍couponUser，couponUser不能关联查询
     *
     * @param currentUserId
     * @param pageIndex
     * @param pageSize
     * @param dto
     * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page<conson.tech.manage.app.entity.vo.ActivityCouponPageVo>
     */
    @Override
    public Page<UserCouponDetailVo> userCouponList(Long currentUserId, Integer pageIndex, Integer pageSize, UserCouponListDto dto) {
        log.info("获取用户我的优惠券列表开始");
        Page<UserCouponDetailVo> page = new Page<>(pageIndex, pageSize);

        page = this.couponsUsersMapper.userCouponList(currentUserId, page, dto);
        if (page.getRecords() != null && page.getRecords().size() > 0) {
            List<UserCouponDetailVo> userCouponDetailVos = page.getRecords().stream().map(couponsUser -> {
                UserCouponDetailVo userCouponDetailVo = new UserCouponDetailVo();
                userCouponDetailVo.setState(couponsUser.getState());
                CouponsBatch couponsBatch = this.couponsBatchMapper.selectOne(new QueryWrapper<CouponsBatch>().eq("id", couponsUser.getId()));
                userCouponDetailVo.setBatchMoney(couponsBatch.getBatchMoney());
                userCouponDetailVo.setBatchName(couponsBatch.getBatchName());
                userCouponDetailVo.setBatchTypeName(couponsBatch.getBatchTypeName());
                userCouponDetailVo.setEndTime(couponsBatch.getEndTime());
                userCouponDetailVo.setStartTime(couponsBatch.getStartTime());
                userCouponDetailVo.setBatchMoney(couponsBatch.getBatchMoney());
                userCouponDetailVo.setLimitMoney(couponsBatch.getLimitMoney());
                userCouponDetailVo.setId(couponsBatch.getId());
                userCouponDetailVo.setProvider(couponsBatch.getProvider());
                userCouponDetailVo.setRangeType(couponsBatch.getRangeType());
                userCouponDetailVo.setUsedDes(couponsBatch.getUsedDes());
                return userCouponDetailVo;
            }).collect(Collectors.toList());
            page.setRecords(userCouponDetailVos);
            return page;
        }

        log.info("获取用户我的优惠券列表完成");
        return null;
    }

    /**
     * 获取用户优惠券配置信息
     *
     * @param currentUserId
     * @param dto
     * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page<conson.tech.manage.app.entity.vo.ActivityCouponPageVo>
     */
    @Override
    public List<UserCouponDetailVo> couponConfigDetails(Long currentUserId, CouponDto dto) {

        log.info("获取用户优惠券配置信息列表开始");

        List<UserCouponDetailVo> userCouponDetailVos = this.couponsUsersMapper.couponConfigDetails(currentUserId, dto);

        if (userCouponDetailVos != null && userCouponDetailVos.size() > 0) {
            userCouponDetailVos = userCouponDetailVos.stream().map(couponUser -> {

                UserCouponDetailVo userCouponDetailVo = new UserCouponDetailVo();
                userCouponDetailVo.setState(couponUser.getState());
                userCouponDetailVo.setId(couponUser.getId());
                CouponsBatch couponsBatch = this.couponsBatchMapper.selectOne(new QueryWrapper<CouponsBatch>().eq("id", couponUser.getId()));
                userCouponDetailVo.setBatchMoney(couponsBatch.getBatchMoney());
                userCouponDetailVo.setBatchName(couponsBatch.getBatchName());
                userCouponDetailVo.setBatchTypeName(couponsBatch.getBatchTypeName());
                userCouponDetailVo.setEndTime(couponsBatch.getEndTime());
                userCouponDetailVo.setStartTime(couponsBatch.getStartTime());
                userCouponDetailVo.setBatchMoney(couponsBatch.getBatchMoney());
                userCouponDetailVo.setLimitMoney(couponsBatch.getLimitMoney());
                userCouponDetailVo.setId(couponsBatch.getId());
                userCouponDetailVo.setProvider(couponsBatch.getProvider());
                userCouponDetailVo.setRangeType(couponsBatch.getRangeType());
                userCouponDetailVo.setUsedDes(couponsBatch.getUsedDes());
                return userCouponDetailVo;
            }).collect(Collectors.toList());
            return userCouponDetailVos;
        }
        log.info("获取用户优惠券配置信息列表完成");
        return null;
    }


    /**
     * 用户领用 限时券活动或领券活动
     *
     * @param currentUserId
     * @param currentUserName
     * @param id
     * @return conson.tech.core.response.Response
     */
    @Override
    @Transactional
    public Response grantCouponTime(Long currentUserId, String currentUserName, Long id) {
        log.info("用户领用限时券活动或领券活动开始");

        UserGrantCouponVo userGrantCouponVo = null;

//        判断活动类型  优惠券类型 1 限时抢 2领券
        CouponsCenter couponsCenter = this.couponsCenterMapper.selectOne(new QueryWrapper<CouponsCenter>().eq("id", id));
        if (couponsCenter == null) {
            throw new BaseException(BusinessMessage.ACTIVITY_CENTER_ID_NOT_EXISTS_ERROR);
        }
//            限时抢领取处理
        //        校验用户领取的次数，是否已经超过了活动领取规定次数
        Integer num = this.couponsUsersMapper.selectCount(new QueryWrapper<CouponsUsers>()
                .eq("user_id", currentUserId)
                .eq("activity_id", couponsCenter.getActivityId())
                .eq("batch_id", couponsCenter.getBatchId()).eq("center_id", couponsCenter.getId()));
        if (num.equals(couponsCenter.getLimitNum())) {
            throw new BaseException(BusinessMessage.COUPON_COUNT_LIMIT_ERROR);
        }
        //        给用户发放优惠券，生成优惠券码
        //      分布式锁
        RLock lock = redissonClient.getLock(CouponsConstants.GRANT_COUPON_LOCK);
//        加锁
        lock.lock();
        Long decrement = 0L;
        try {
            decrement = redisTemplate.opsForValue().decrement(CouponsConstants.ACTIVITY_COUPON_PREFIX + couponsCenter.getActivityId() + ":" + couponsCenter.getBatchId() + ":" + couponsCenter.getId());
        } catch (Exception e) {
            throw new BaseException(BusinessMessage.COUPON_STOCK_DECREMENT_ERROR);
        } finally {
            lock.unlock();
            if (decrement != null && decrement > 0) {
//           给用户发放优惠券，生成优惠券码
                //        1.生成优惠券码
                String code = TimeUtils.getGuid();
                CouponsUsers couponsUsers = new CouponsUsers();
                couponsUsers.setCouponsCode(code);
                couponsUsers.setBatchId(couponsCenter.getBatchId());
                couponsUsers.setActivityId(Long.parseLong(couponsCenter.getActivityId().toString()));
                couponsUsers.setCreateTime(LocalDateTime.now());
                couponsUsers.setCreateBy(currentUserId);
                couponsUsers.setState(0);
                couponsUsers.setCenterId(couponsCenter.getId());

                CouponsBatch couponsBatch = this.couponsBatchMapper.selectById(couponsCenter.getBatchId());
//            设置优惠券的有效日期
                if (couponsBatch.getTimeLimit() == 1) {
                    couponsUsers.setStartTime(LocalDateTime.now());
                    couponsUsers.setEndTime(LocalDateTime.now().plusDays(couponsBatch.getRelativeTime()));
                }
                if (couponsBatch.getTimeLimit() == 0) {
                    couponsUsers.setStartTime(couponsBatch.getStartTime());
                    couponsUsers.setEndTime(couponsBatch.getEndTime());
                }
                couponsUsers.setUserId(currentUserId);
                couponsUsersMapper.insert(couponsUsers);

                // 插入优惠券发放日志表
                CouponsUsersLog couponsUsersLog = new CouponsUsersLog();
                couponsUsersLog.setCreateTime(LocalDateTime.now());
                couponsUsersLog.setCouponsId(couponsUsers.getId());
                couponsUsersLog.setCouponsCode(couponsUsers.getCouponsCode());
                couponsUsersLog.setCouponsState(0);
                couponsUsersLog.setCreateName(currentUserName);
                couponsUsersLog.setOperateDesc("用户领取优惠券");
                this.couponsUsersLogMapper.insert(couponsUsersLog);

//            将结果返回
                userGrantCouponVo = new UserGrantCouponVo();
                userGrantCouponVo.setCouponsCode(couponsUsers.getCouponsCode());
                userGrantCouponVo.setStartTime(couponsUsers.getStartTime());
                userGrantCouponVo.setEndTime(couponsUsers.getEndTime());
            }
        }
        //        插入优惠券发放日志表

        log.info("用户领用限时券活动或领券活动结束");
        return Response.success(userGrantCouponVo);
    }

    /**
     * 领券中心用户优惠券列表-领券和秒杀(限时抢)
     *
     * @param currentUserId
     * @return conson.tech.manage.app.entity.vo.AppSeckillAndReceivceCoupons
     */
    @Override
    public AppSeckillAndReceivceCoupons queryCoupons(Long currentUserId) throws ParseException {
        log.info("查询领券中心用户优惠券列表-领券和秒杀(限时抢)开始");

        AppSeckillAndReceivceCoupons coupons = new AppSeckillAndReceivceCoupons();
        int num = -1;
        List<CouponSeckillVo> list = this.couponsCenterMapper.queryCouponsSeckillCenter(num, TimeUtils.localDateTimeToString(LocalDateTime.now()));
        if (list != null && list.size() > 0) {
            for (CouponSeckillVo couponSeckillVo : list) {
                LocalDateTime todayPlusHour = TimeUtils.getTodayPlusHour(LocalDate.now(), couponSeckillVo.getBeginTime().getHour());
                LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
                boolean res = LocalDateTime.now().equals(todayPlusHour) || LocalDateTime.now().isAfter(todayPlusHour);
                boolean res1 = LocalDateTime.now().equals(endTime) || LocalDateTime.now().isBefore(endTime);
                if (res && res1) {
//                    1:可领取，2:已领取，3:已抢光，4:未开始
                    couponSeckillVo.setState(1);
                    //获取redis缓存，读取剩余数量
                    String s = redisTemplate.opsForValue().get(CouponsConstants.ACTIVITY_COUPON_PREFIX + num + ":" + couponSeckillVo.getBatchId() + ":" + couponSeckillVo.getId());
                    if (s != null && s.equals("0")) {
                        couponSeckillVo.setState(3);
                    }
                    int userNum = this.couponsUsersMapper.queryUserName(currentUserId, couponSeckillVo.getBatchId(), couponSeckillVo.getId());
                    if (userNum >= couponSeckillVo.getLimitNum()) {
                        couponSeckillVo.setState(2);
                    }
                    coupons.setCouponSeckillVos(list);
                } else {
//                     还有多长时间开抢,秒级
                    long now = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                    long begin = LocalDateTime.of(LocalDate.now(), couponSeckillVo.getBeginTime()).toInstant(ZoneOffset.of("+8")).toEpochMilli();
                    long remainTime = (begin - now) / 1000;
                    couponSeckillVo.setRemainTime(remainTime);
                    couponSeckillVo.setState(4);
                }
            }
        }

        int num1 = -2;
        List<CouponReceiveVo> list1 = this.couponsCenterMapper.queryCouponsReceiveCenter(num1, TimeUtils.localDateTimeToString(LocalDateTime.now()));
        if (list1 != null && list1.size() > 0) {
            for (CouponReceiveVo couponReceiveVo : list1) {
                boolean res = LocalDateTime.now().equals(couponReceiveVo.getStartTime()) || LocalDateTime.now().isAfter(couponReceiveVo.getStartTime());
                boolean res1 = LocalDateTime.now().equals(couponReceiveVo.getEndTime()) || LocalDateTime.now().isBefore(couponReceiveVo.getEndTime());
                if (res && res1) {
//                    1:可领取，2:已领取，3:已抢光，4:未开始
                    couponReceiveVo.setState(1);
                    //获取redis缓存，读取剩余数量 activity:conpon:-2:85:44
                    Boolean aBoolean = redisTemplate.hasKey(CouponsConstants.ACTIVITY_COUPON_PREFIX + num1 + ":" + couponReceiveVo.getBatchId().toString() + ":" + couponReceiveVo.getId().toString());
                    if (aBoolean != null && !aBoolean) {
                        couponReceiveVo.setState(3);
                    }
                    int userNum = this.couponsUsersMapper.queryUserName(currentUserId, couponReceiveVo.getBatchId(), couponReceiveVo.getId());
                    if (userNum >= couponReceiveVo.getLimitNum()) {
                        couponReceiveVo.setState(2);
                    }
                    coupons.setCouponReceiveVos(list1);
                } else {
                    couponReceiveVo.setState(4);
                }
            }
        }
        log.info("用户领券中心用户优惠券列表-领券和秒杀(限时抢)结束");
        return coupons;
    }

}
